\subsection{数制系统}

因为大多数人都有10根手指，所以人们可能已经习惯了十进制的数值系统。
尽管，在自然界和数学界，10是一个毫无特殊意义的数值。
数字电路使用二进制作为其数制系统: 0表示线路中没有电流，而1表示线路中存在电流。
二进制中的10对应于十进制的2; 二进制中的100对应于十进制的4，等等。

如果数制系统使用了10种不同的数字作为数位，那么我们说，它以10为\IT{基数}(radix/base)。
二进制系统以2为\IT{基数}。

重申一下:
1) \IT{数字}指的就是数值，而\IT{数位}是书写系统中的一个术语，并且通常只有一位。
2) 在转换到其他基数的数制系统时，数字(数值)本身没有改变，改变的是表示方法。

如何将一个数字从转换为其他基数的数制系统呢?

位置计数法是一种非常常见的表示法，这表示，数值的每一数位，都有着和其位置相关的权重。
如果2出现在个位上，那它表示的就是2。
如果它出现在十位上，那它表示的就是20。

$1234$代表什么呢?

$10^3 \cdot 1 + 10^2 \cdot 2 + 10^1 \cdot 3 + 1 \cdot 4$ = 1234 或者
$1000 \cdot 1 + 100 \cdot 2 + 10 \cdot 3 + 4 = 1234$

对于2进制来说，表示的方法也是相同的，但是基数是2而不是10。
那0b101011代表什么呢?

$2^5 \cdot 1 + 2^4 \cdot 0 + 2^3 \cdot 1 + 2^2 \cdot 0 + 2^1 \cdot 1 + 2^0 \cdot 1 = 43$ 或
$32 \cdot 1 + 16 \cdot 0 + 8 \cdot 1 + 4 \cdot 0 + 2 \cdot 1 + 1 = 43$

与位置表示法对立的，是非位置表示法，如罗马数字系统。
\footnote{关于数制系统的演变过程，详见\InSqBrackets{\TAOCPvolII{}, 195--213.}}.
人们选择了位置表示法，可能是因为在纸面上手算基本运算(加法、乘法等等)更加方便吧。

实际上，二进制的数值也可以用在学校学到的方法来进行加减运算等等，但是只有2种数位(逢2进1)可以使用。

二进制数值在代码和转储数据中的表示都很麻烦，所以十六进制因此被广泛使用。
十六进制使用数值0..9以及6个拉丁字母A..F作为其数位。
每一位十六进制数对应了4个比特(即4个二进制位)，所以在二进制和十六进制之间的转换是十分轻松的事情，哪怕在头脑中转换也是一样。

\begin{center}
\begin{longtable}{ | l | l | l | }
\hline
\HeaderColor 16进制 & \HeaderColor 2进制 & \HeaderColor 10进制 \\
\hline
0	&0000	&0 \\
1	&0001	&1 \\
2	&0010	&2 \\
3	&0011	&3 \\
4	&0100	&4 \\
5	&0101	&5 \\
6	&0110	&6 \\
7	&0111	&7 \\
8	&1000	&8 \\
9	&1001	&9 \\
A	&1010	&10 \\
B	&1011	&11 \\
C	&1100	&12 \\
D	&1101	&13 \\
E	&1110	&14 \\
F	&1111	&15 \\
\hline
\end{longtable}
\end{center}

如何确定数值使用的基数呢?

十进制系统往往就像它本身表示的那样，例如，1234。一些汇编程序允许对基数10进行强调，会在这个十进制数的后面添加一个小写字母"d"的后缀: 1234d。

二进制数有时会使用"0b"前缀: 0b100110111 (\ac{GCC}有一个对此种表示法的“非标准的语言扩展”\footnote{\url{https://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html}})。
另一种表示法是: 使用小写字母"b"的后缀，例如: 100110111b。
在本书中，我会使用"0b"前缀来表示二进制数。

在\CCpp和其他\ac{程序语言}中，十六进制数通常带有"0x"前缀: 0x1234ABCD。
或者使用小写字母"h"的后缀: 1234ABCDh —— 这是汇编程序和调试器中的惯用表示法。
如果一个数字以A..F开始，那么通常我们会在前面添加一个"0": 0ABCDEFh。
% TBT
在本书中，我会使用"0x"前缀来表示十六进制数。

我们是否应该学会在头脑中做数制转换呢?
对于1位十六进制的数，也许我们可以轻松记住。
但是对于比较大的数字，并不必难为自己。

对大多数人来说，最常见到十六进制数的地方，就是在\ac{URL}中了。
这也是非拉丁字符通常使用的编码手段。
举例来说:
\url{https://en.wiktionary.org/wiki/na\%C3\%AFvet\%C3\%A9}是一个关于\q{naïveté}单词的Wiktionary页面的\ac{URL}。

\subsubsection{八进制}

在过去的计算机系统中，另一个被重度使用的数制系统就是八进制了: 使用了0..7作为其数位，且每一位对应于3个比特，所以它也很容易(和二进制数)进行数制转换。
如今，在大多数情况下八进制已经被十六进制取代，但令人惊讶的是，*NIX中有一个仍被许多人使用的功能，还使用八进制数作为其参数: \TT{chmod}。

\myindex{UNIX!chmod}
*NIX用户都知道，\TT{chmod}命令的参数可以是一个3位的数值。第一位表示文件所属者的权限，第二位表示文件所属者同组用户的权限，第三位表示其他用户的权限。
而每一位数位，在二进制表示的意义如下:

\begin{center}
\begin{longtable}{ | l | l | l | }
\hline
\HeaderColor 10进制 & \HeaderColor 2进制 & \HeaderColor 含义 \\
\hline
7	&111	&\textbf{rwx} \\
6	&110	&\textbf{rw-} \\
5	&101	&\textbf{r-x} \\
4	&100	&\textbf{r-{}-} \\
3	&011	&\textbf{-wx} \\
2	&010	&\textbf{-w-} \\
1	&001	&\textbf{-{}-x} \\
0	&000	&\textbf{-{}-{}-} \\
\hline
\end{longtable}
\end{center}

每一位都被映射到一个操作上: 可读/可写/可执行。

我在这里讨论\TT{chmod}，是想说明，整个参数可以使用一个八进制数表示。
例如说，644。
当你执行\TT{chmod 644 file}时，你为文件所属者设定了读/写权限、为同组用户和其他用户设定了读权限。
我们将八进制的644转换为二进制，将变成\TT{110100100}，以3个数为一组，则变成\TT{110 100 100}。

从每个三元组中，我们可以看出它对文件所属者/同组用户/其他用户指定的权限: 第一组代表\TT{rw-}，第二组代表\TT{r--}，第三组代表\TT{r--}。

八进制系统曾经在PDP-8这样的旧电脑上被广泛使用，因为字长可能为12、24或36比特，而这些数都能被3整除，所以在当时的环境下，八进制是很自然的选择。
如今，电脑的字长/寻址长度通常都是16、32或64比特，而这些数都能被4整除，所以十六进制的应用更为普遍。

八进制系统被所有标准的\CCpp编译器所支持。
八进制的表示可能会导致疑惑，因为八进制数通常使用"0"作为前缀。例如，0377对应的是十进制的255。
而有时你会因为拼写错误，把"9"误输入成"09"，而编译器会阻止你这么做。
GCC可能会给出以下的报错信息:\\
\TT{error: invalid digit "9" in octal constant}(错误: 八进制常量中出现非法的数位"9")

\subsubsection{整除性}

当年看见一个像120一样的十进制数时，你能很快推断出它被10整除，因为它的最后一位是0。
同理可得，123400被100整除，因为它的末两位都是0。

相似地，十六进制数0x1230能被0x10(即16)整除，0x123000可以被0x1000(即4096)整除，等等。

二进制数0b1000101000能被0b1000(即8)整除，等等。

这一属性通常被用于快速确认一块内存是否对齐于某个边界值。
例如，在\ac{PE}文件中的节头部分通常位于一个末尾为3个0的地址: 0x41000，0x10001000，等等。
这是因为所有几乎所有的\ac{PE}节都按0x1000(即4096)字节对齐。

\subsubsection{多精度运算和基数}

\index{RSA}
多精度运算使用大数，每一个变量都使用多个字节存储。
例如，RSA公钥算法中的密钥，即公钥和私钥，都可以扩展到4096比特或者更多的位数。

在\InSqBrackets{\TAOCPvolII, 265}中我们可以发现如下的表示法: 当你使用多个字节存储多精度数值的时候，整个数值可以被看作以$2^8=256$为基数的数值，而其中的每一个数位都被提升为对应位置的权重。
类似地，如果你将多精度数值存储在几个32位整数中，数值的每一位就对应了32比特的一位，那么就可以看做这个数值以$2^{32}$为基数储存。

\subsubsection{读法}

非10进制数通常一位一位地读: “一 零 零 一 一...”。
像“十”、“千”这样的词通常不在读数时使用，因为这样会与十进制的情况发生混淆。

\subsubsection{浮点数}

为了将浮点数和整数取费开，通常在浮点数的结尾添加".0"，例如$0.0$，$123.0$等等。
